package island.gotenyama.wand.web;

import island.gotenyama.wand.bean.Query;
import island.gotenyama.wand.exception.FatalException;
import island.gotenyama.wand.exception.InputException;
import island.gotenyama.wand.exception.LogicalException;
import island.gotenyama.wand.field.PasswordHintField;
import island.gotenyama.wand.field.ReutersNewsField;
import island.gotenyama.wand.logic.ValidationLogic;
import island.gotenyama.wand.utils.AppsExceptionUtils;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.MessageSource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * 基底コントローラクラス
 * 
 * @author tk
 */
@Transactional()
public abstract class ApplicationController implements Serializable {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * LOGGER
	 */
	private static final Log LOG = LogFactory.getLog(ApplicationController.class);

	/**
	 * 共通エラー画面
	 */
	public static final String ERROR_PAGE = "cmn/error.vm";

	/**
	 * ログメッセージ
	 */
	public static final String LOG_CONV_EXP_MESSAGE = "BindingResultに格納されたエラーを例外としてスロー";

	/**
	 * エラーメッセージキーリスト
	 */
	public static final String ERROR_MESSAGE_LIST_KEY = "errorMessageKeyList";

	/**
	 * 次ページを特定するキー値
	 */
	public static final String NEXT_PAGE_KEY = "ApplicationController.NEXT_PAGE_KEY";
	
	/**
	 * 
	 */
	public final String INFO_MESSAGE_REQUEST_KEY = "errorMessage";
	
	/**
	 * 
	 */
	public final String ERROR_MESSAGE_REQUEST_KEY = "errorMessage";
	
	/**
	 * 
	 */
	protected Locale locale = Locale.JAPAN;
	
	/**
	 * 
	 */
	@Autowired(required = true)
	@Qualifier("messageSource")
	protected MessageSource messageSource;

	/**
	 * 
	 */
	@Autowired(required = true)
	protected ValidationLogic val;

	/**
	 * 
	 */
	protected Query<PasswordHintField> passwordHintQ;

	/**
	 * 
	 */
	protected Query<ReutersNewsField> reutersNewsFieldQ;

	/**
	 * 
	 * 
	 * @param dataSource　データソース
	 */
	@Autowired
	public void initQuery(@Qualifier(value = "dataSource") DataSource dataSource) {
		passwordHintQ = new Query<PasswordHintField>(dataSource, PasswordHintField.class);
		reutersNewsFieldQ = new Query<ReutersNewsField>(dataSource, ReutersNewsField.class);
	}

	/**
	 * デフォルトのデータバインダ
	 * 
	 * @param binder データバインダ
	 */
	public static void defaultInitBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
	}

	/**
	 * 次ページの遷移先を設定する。
	 * 
	 * @param nextPage 次ページのパス
	 */
	protected void setNextPage(String nextPage) {
		if (nextPage != null) {
			HttpServletRequest request = AllRoundInterceptor.getRequest();
			request.setAttribute(NEXT_PAGE_KEY, nextPage);
		}
	}

	/**
	 * 次ページの遷移先を取得する。
	 * 
	 * @return 次ページの遷移先
	 */
	public String getNextPage() {
		HttpServletRequest request = AllRoundInterceptor.getRequest();
		String nextPage = (String) request.getAttribute(NEXT_PAGE_KEY);
		if (nextPage == null) {
			nextPage = getDefaultPagePath();
		}
		return nextPage;
	}

	/**
	 * お知らせメッセージを追加する。
	 * 
	 * @param mm モデルマップ
	 * @param messageCode メッセージコード
	 */
	protected void addInfoMessage(ModelMap mm, String messageCode) {
		mm.put(INFO_MESSAGE_REQUEST_KEY, messageSource.getMessage(messageCode, null, locale));
	}

	/**
	 * お知らせメッセージを追加する。
	 * 
	 * @param mv モデルビュー
	 * @param messageCode メッセージコード
	 */
	protected void addInfoMessage(ModelAndView mv, String messageCode) {
		mv.addObject(INFO_MESSAGE_REQUEST_KEY, messageSource.getMessage(messageCode, null, locale));
	}

	/**
	 * エラーメッセージを追加する。
	 * 
	 * @param mm モデルマップ
	 * @param messageCode メッセージコード
	 */
	protected void addErrorMessage(ModelMap mm, String messageCode) {
		mm.put(ERROR_MESSAGE_REQUEST_KEY, messageSource.getMessage(messageCode, null, locale));
	}

	/**
	 * エラーメッセージを追加する。
	 * 
	 * @param mv モデルビュー
	 * @param messageCode メッセージコード
	 */
	protected void addErrorMessage(ModelAndView mv, String messageCode) {
		mv.addObject(ERROR_MESSAGE_REQUEST_KEY, messageSource.getMessage(messageCode, null, locale));
	}

	/**
	 * ページパスを取得する。
	 * 
	 * @return ページパスを表す文字列
	 */
	protected abstract String getDefaultPagePath();

	/**
	 * 論理例外が発生した場合にコールされる例外ハンドラーメソッド
	 * 
	 * @param e 論理例外
	 * @param request リクエスト情報
	 * @return モデル情報
	 */
	@ExceptionHandler(LogicalException.class)
	public ModelAndView handleLogicalException(LogicalException e, HttpServletRequest request) {
		if (LOG.isWarnEnabled()) {
			LOG.warn(AppsExceptionUtils.buildLogMessage(e));
		}
		ModelAndView mav = null;
		String nextPage = getNextPage();
		if (nextPage != null) {
			mav = new ModelAndView(nextPage);
		} else {
			mav = new ModelAndView(ERROR_PAGE);
		}
		addErrorMessage(mav, "info.error");
		mav.addObject(ERROR_MESSAGE_LIST_KEY, e.getConveyance().getMessageKeyList());
		return mav;
	}

	/**
	 * 検証例外が発生した場合にコールされる例外ハンドラーメソッド
	 * 
	 * @param e 検証例外
	 * @param request リクエスト情報
	 * @return モデル情報
	 */
	@ExceptionHandler(InputException.class)
	public ModelAndView handleInputException(InputException e, HttpServletRequest request) {
		if (LOG.isInfoEnabled()) {
			LOG.info(AppsExceptionUtils.buildLogMessage(e));
		}
		ModelAndView mav = null;
		String nextPage = getNextPage();
		if (nextPage != null) {
			mav = new ModelAndView(nextPage);
		} else {
			mav = new ModelAndView(ERROR_PAGE);
		}
		addErrorMessage(mav, "info.input");
		mav.addObject(ERROR_MESSAGE_LIST_KEY, e.getConveyance().getMessageKeyList());
		return mav;
	}

	/**
	 * システム例外が発生した場合にコールされる例外ハンドラーメソッド
	 * 
	 * @param e 論理例外
	 * @param request リクエスト情報
	 * @return モデル情報
	 */
	@ExceptionHandler(FatalException.class)
	public ModelAndView handleFatalException(FatalException e, HttpServletRequest request) {
		if (LOG.isErrorEnabled()) {
			LOG.error(AppsExceptionUtils.buildLogMessage(e));
		}
		ModelAndView mav = null;
		String nextPage = getNextPage();
		if (nextPage != null) {
			mav = new ModelAndView(nextPage);
		} else {
			mav = new ModelAndView(ERROR_PAGE);
		}
		addErrorMessage(mav, "info.fatal");
		mav.addObject(ERROR_MESSAGE_LIST_KEY, e.getConveyance().getMessageKeyList());
		return mav;
	}
}
